/**
 * crawler
 *
 * outlook@China software studio
 * Copyright (c) 2008-2010. All Rights Reserved.
 * http://www.outlook.com
 */

package org.outlook.crawler.util.parser;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML.Attribute;
import javax.swing.text.html.HTML.Tag;
import javax.swing.text.html.HTMLEditorKit.ParserCallback;
import javax.swing.text.html.parser.ParserDelegator;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.outlook.common.log.LogUtil;

/**
 * function:
 * 
 * @author Watson email: watson.wu@hotmail.com
 * @date 2011
 */
public class LinkParser {
	
	Log logger = LogFactory.getLog(LinkParser.class);

	private static final String[] VOID_INVOKER = new String[]{"javascript:", "mailto:", "#"};
	private Pattern filter;
	
	public LinkParser(String filters) {
		if(StringUtils.isNotBlank(filters)) {
			filter = Pattern.compile(filters);
		}
	}

	public List<String> parse(Reader reader) throws IOException {
		final List<String> list = new ArrayList<String>();
		ParserDelegator parserDelegator = new ParserDelegator();
		ParserCallback parserCallback = new ParserCallback() {

			public void handleStartTag(Tag tag, MutableAttributeSet attribute, int pos) {
				String address = null;
				if (attribute.getAttribute(Attribute.HREF) != null) {
					address = (String) attribute.getAttribute(Attribute.HREF);
				} else if(attribute.getAttribute(Attribute.SRC) != null) {
					address = (String) attribute.getAttribute(Attribute.SRC);
				} else if(attribute.getAttribute(Attribute.CONTENT) != null) {
					address = (String) attribute.getAttribute(Attribute.CONTENT);
				}
				if(StringUtils.isNotEmpty(address)) {
//					if(isLink(address) && !isIgnore(address))
					if(isIgnore(address))
						LogUtil.debug(logger, pos, " filter a link", address);
					if(isLink(address))
						list.add(address);
				}
			}

		};

		parserDelegator.parse(reader, parserCallback, true);

		return list;
	}
	public List<LinkDim> parseAll(Reader reader, final StringBuffer html) throws IOException {
		final List<LinkDim> list = new ArrayList<LinkDim>();
		ParserDelegator parserDelegator = new ParserDelegator();
		ParserCallback parserCallback = new ParserCallback() {
//			boolean matches = false;
//			Dimension dimension = null;

			public void handleStartTag(Tag tag, MutableAttributeSet attribute, int pos) {
				handleLink(html, list, attribute, pos);
			}

			private void handleLink(final StringBuffer html, final List<LinkDim> list,
					MutableAttributeSet attribute, int pos) {
				String address = null;
				if (attribute.getAttribute(Attribute.HREF) != null) {
					address = (String) attribute.getAttribute(Attribute.HREF);
				} else if(attribute.getAttribute(Attribute.SRC) != null) {
					address = (String) attribute.getAttribute(Attribute.SRC);
				} else if(attribute.getAttribute(Attribute.CONTENT) != null) {
					address = (String) attribute.getAttribute(Attribute.CONTENT);
				}
				if(StringUtils.isNotEmpty(address)) {
					//if(isLink(address) && !isIgnore(address))
					if(isLink(address)) {
						LinkDim linkDim = new LinkDim();
						linkDim.setUrl(address);
						linkDim.setStart(pos);
						int end = getEndTagOffset(html, pos);
						linkDim.setEnd(end);
						list.add(linkDim);
						//matches = true;
					}
				}
			}

			@Override
			public void handleSimpleTag(Tag t, MutableAttributeSet attribute, int pos) {
				handleLink(html, list, attribute, pos);
			}

			private int getEndTagOffset(StringBuffer html, int pos) {
				int index1 = html.indexOf("</", pos);
				int index2 = html.indexOf("/>", pos);
				
				return (index1>0?index1:Integer.MAX_VALUE)<(index2>0?index2:Integer.MAX_VALUE) ? index1:index2;
			}

			/**
			 * could not handle "<link src="..." />"
			 */
//			public void handleEndTag(Tag t, int pos) {
//				if(matches && dimension!=null) {
//					dimension.setEnd(pos);
//					matches = false;
//				}
//			}

		};

		parserDelegator.parse(reader, parserCallback, true);

		return list;
	}

	public boolean isIgnore(String address) {
		return filter == null ? false:filter.matcher(address).matches();
	}

	public boolean isLink(String address) {
		for(String voidAddress : VOID_INVOKER) {
			if(address.startsWith(voidAddress))
				return false;
		}
		return true;
	}

}
